package robot

import (
	"context"
	"encoding/base64"
	"strconv"
	"time"

	"device-admin/config/mysql"
	"device-admin/config/redis"
	"device-admin/internal/app/grpc/protoc/robotc"
	"device-admin/internal/app/grpc/service/pubsub"
	"device-admin/internal/app/web/service/cmd"
	"device-admin/internal/define"
	"device-admin/internal/models"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"google.golang.org/protobuf/proto"
)

type Point struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

// PushFlowCMDPack 推流命令封装
func PushFlowCMDPack(robotID string, username string, camType int) error {
	pushFlowCMD := &robotc.PushFlowCMD{}
	pushFlowCMD.NPushStatus = 1
	pushFlowCMD.CamType = int32(camType) // 摄像头类型
	if camType == 1 {
		pushFlowCMD.Streamaddr = "rtmp://101.37.124.215:1985/myapp/" + robotID + "charging" // 推流地址
	} else if camType == 2 {
		pushFlowCMD.Streamaddr = "rtmp://101.37.124.215:1985/myapp/" + robotID + "rgbd"
	}
	// 额外数据
	exData := &robotc.CmdExtend{}
	exData.LlTimeStamp = time.Now().UnixMilli()
	exData.StrUserName = []byte(username)
	pushFlowCMD.ExData = exData
	dataBytes, err := proto.Marshal(pushFlowCMD)
	if err != nil {
		logx.Error(context.Background(), "PushFlowCMDPack", logx.Err(err))
	}
	// 标记正在推流（定时保活）
	redisCtx, redisCtxCancel := context.WithTimeout(context.Background(), time.Second*3)
	defer redisCtxCancel()
	redis.NewDB().Set(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(camType), time.Now().Unix(), time.Second*2) // 标记推流标志
	err = pubsub.CMDPublish(robotID, "95", "pushFlow", base64.StdEncoding.EncodeToString(dataBytes))
	// 标记正在推流
	redisCtx, redisCancel := context.WithTimeout(context.Background(), time.Second*3)
	defer redisCancel()
	redis.NewDB().HSet(redisCtx, define.RedisRobotRTInfo+robotID, define.IsPushingFlow+"-"+strconv.Itoa(camType), 1)
	return err
}

// PushFlow2Client 请求推流
func (s *Server) PushFlow2Client(param *robotc.PushFlowCMDParam, ss robotc.BaseService_PushFlow2ClientServer) error {
	robotID := GetRobotID(ss.Context())
	chanMapKey := "pushFlow@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "PushFlow2Client-connected", logx.String("robot_id", robotID))
	for {
		select {
		case <-ss.Context().Done():
			logx.Info(context.Background(), "PushFlow2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			pushFlowCMD := &robotc.PushFlowCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, pushFlowCMD)
			err = ss.Send(pushFlowCMD)
			if err != nil {
				logx.Error(context.Background(), "PushFlow2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "PushFlow2Client-sendCMD", logx.String("robot_id", robotID))
			// 标记推流标志
			timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*3)
			redis.NewDB().Set(timeoutCtx, define.RedisISUnderMonitoring+robotID, time.Now().Unix(), time.Second*10)
			cancel()
		}
	}
}

// StartTaskCMDPack 执行任务命令封装
func StartTaskCMDPack(robotID, mapName, taskName string, runCount int, username string) error {
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	if mapName == "" {
		return errorx.New("地图名称不能为空", -1)
	}
	if taskName == "" {
		return errorx.New("任务名称不能为空", -1)
	}
	if runCount == 0 {
		return errorx.New("运行次数不能为空", -1)
	}
	startTaskCMD := &robotc.StartTaskCMD{}
	startTaskCMD.StrMapName = []byte(mapName)
	startTaskCMD.StrTaskName = []byte(taskName)
	startTaskCMD.NrunCount = int32(runCount)
	// 额外数据
	exData := &robotc.CmdExtend{}
	exData.LlTimeStamp = time.Now().UnixMilli()
	exData.StrUserName = []byte(username)
	startTaskCMD.ExData = exData
	dataBytes, err := proto.Marshal(startTaskCMD)
	if err != nil {
		logx.Error(context.Background(), "StartTaskCMDPack", logx.Err(err))
	}
	return pubsub.CMDPublish(robotID, "95", "startTask", base64.StdEncoding.EncodeToString(dataBytes))
}

// StartTask2Client 执行任务
func (s *Server) StartTask2Client(in *robotc.StartTaskCMDParam, rsp robotc.BaseService_StartTask2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "startTask@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "StartTask2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "StartTask2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			startTaskCMD := &robotc.StartTaskCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, startTaskCMD)
			if err := rsp.Send(startTaskCMD); err != nil {
				logx.Error(context.Background(), "StartTask2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "StartTask2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// PauseCurrentTaskPack 暂停任务命令封装
func PauseCurrentTaskCMDPack(robotID, taskID, username string) error {
	pauseTaskCmd := &robotc.PauseCurrentTaskCMD{}
	pauseTaskCmd.StrTaskId = []byte(taskID)
	// 额外数据
	exData := &robotc.CmdExtend{}
	exData.LlTimeStamp = time.Now().UnixMilli()
	exData.StrUserName = []byte(username)
	pauseTaskCmd.ExData = exData
	dataBytes, err := proto.Marshal(pauseTaskCmd)
	if err != nil {
		logx.Error(context.Background(), "PauseCurrentTaskCMDPack", logx.Err(err))
	}
	err = pubsub.CMDPublish(robotID, "95", "pauseTask", base64.StdEncoding.EncodeToString(dataBytes))
	return err
}

// PauseCurrentTask2Client 暂停任务
func (s *Server) PauseCurrentTask2Client(in *robotc.PauseCurrentTaskCMDParam, rsp robotc.BaseService_PauseCurrentTask2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "pauseTask@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "PauseCurrentTask2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "PauseCurrentTask2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			pauseTaskCMD := &robotc.PauseCurrentTaskCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, pauseTaskCMD)
			if err := rsp.Send(pauseTaskCMD); err != nil {
				logx.Error(context.Background(), "PauseCurrentTask2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "PauseCurrentTask2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// EndTaskPack 停止任务命令封装
func EndTaskCMDPack(robotID, taskID, username string) {
	endTaskCmd := &robotc.EndTaskCMD{}
	endTaskCmd.StrTaskId = []byte(taskID)
	// 额外数据
	exData := &robotc.CmdExtend{}
	exData.LlTimeStamp = time.Now().UnixMilli()
	exData.StrUserName = []byte(username)
	endTaskCmd.ExData = exData
	dataBytes, err := proto.Marshal(endTaskCmd)
	if err != nil {
		logx.Error(context.Background(), "EndTaskCMDPack", logx.Err(err))
	}
	pubsub.CMDPublish(robotID, "95", "endTask", base64.StdEncoding.EncodeToString(dataBytes))
}

// EndTask2Client 终止任务
func (s *Server) EndTask2Client(in *robotc.EndTaskCMDParam, rsp robotc.BaseService_EndTask2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "endTask@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "EndTask2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "EndTask2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			endTaskCMD := &robotc.EndTaskCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, endTaskCMD)
			if err := rsp.Send(endTaskCMD); err != nil {
				logx.Error(context.Background(), "EndTask2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "EndTask2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// AutoRelocationCMDPack 自动重定位命令封装
func AutoRelocationCMDPack(robotID string, username string) error {
	// 参数检查
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	cmd := &robotc.RelocationCMD{
		ExData: &robotc.CmdExtend{},
	}
	cmd.ExData.LlTimeStamp = time.Now().UnixMilli()
	cmd.ExData.StrUserName = []byte(username)
	dataBytes, _ := proto.Marshal(cmd)
	err := pubsub.CMDPublish(robotID, "95", "relocation", base64.StdEncoding.EncodeToString(dataBytes))
	return err
}

// Relocation2Client 自动重定位
func (s *Server) Relocation2Client(in *robotc.RelocationCMDParam, rsp robotc.BaseService_Relocation2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "relocation@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "Relocation2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "Relocation2Client-disconnect", logx.String("robot_id", robotID))
			// 取消订阅
			return nil
		case data := <-robotCMDChan:
			cmd := &robotc.RelocationCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, cmd)
			if err := rsp.Send(cmd); err != nil {
				logx.Error(context.Background(), "Relocation2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "Relocation2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// PoseRelocation 重定位命令封装
func PoseRelocationCMDPack(robotID string, posX, posY, posTh float64, username string) error {
	// 参数检查
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	// 判断设备是否存在
	var count int64
	mysql.NewDB().
		Model(&models.Device{}).
		Where(models.Device{
			RobotID: robotID,
		}).Count(&count)
	if count <= 0 {
		return errorx.New("设备不存在", -1)
	}
	relocationCmd := &robotc.PoseRelocationCMD{}
	relocationCmd.PosX = float32(posX)
	relocationCmd.PosY = float32(posY)
	relocationCmd.PosTh = float32(posTh)
	// 额外数据
	exData := &robotc.CmdExtend{}
	exData.LlTimeStamp = time.Now().UnixMilli()
	exData.StrUserName = []byte(username)
	relocationCmd.ExData = exData
	dataBytes, err := proto.Marshal(relocationCmd)
	if err != nil {
		logx.Error(context.Background(), "PoseRelocationCMDPack", logx.Err(err))
		return err
	}
	err = pubsub.CMDPublish(robotID, "95", "poseRelocation", base64.StdEncoding.EncodeToString(dataBytes))
	return err
}

// 重定位
func (s *Server) PoseRelocation2Client(in *robotc.PoseRelocationCMDParam, rsp robotc.BaseService_PoseRelocation2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "poseRelocation@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "PoseRelocation2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "PoseRelocation2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			cmd := &robotc.PoseRelocationCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, cmd)
			if err := rsp.Send(cmd); err != nil {
				logx.Error(context.Background(), "PoseRelocation2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "PoseRelocation2Client-sendCMD", logx.String("robot_id", robotID), logx.Any("data", cmd))
		}
	}
}

// TaskEffectivenessTaskCMDPack 恢复时段任务命令封装
func TaskEffectivenessTaskCMDPack(robotID string, mapName string, taskName string, taskEffection string) error {
	cmd := &robotc.TaskEffectivenessCMD{}
	cmd.StrMapName = []byte(mapName)
	cmd.StrTaskName = []byte(taskName)
	cmd.StrTaskEffection = []byte(taskEffection)
	dataBytes, err := proto.Marshal(cmd)
	if err != nil {
		logx.Error(context.Background(), "taskEffectiveness", logx.Err(err))
	}
	err = pubsub.CMDPublish(robotID, "95", "taskEffectiveness", base64.StdEncoding.EncodeToString(dataBytes))
	return err
}

func (s *Server) TaskEffectiveness2Client(in *robotc.TaskEffectivenessCMDParam, rsp robotc.BaseService_TaskEffectiveness2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "taskEffectiveness@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "TaskEffectiveness2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "TaskEffectiveness2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			cmd := &robotc.TaskEffectivenessCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, cmd)
			if err := rsp.Send(cmd); err != nil {
				logx.Error(context.Background(), "TaskEffectiveness2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "TaskEffectiveness2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// ResumeTaskCMDPack 恢复时段任务命令封装
func ResumeExceptionTaskCMDPack(robotID, username string, exceptionType string) error {
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	resumeTaskCmd := &robotc.ResumeTaskCMD{}
	resumeTaskCmd.StrResver = []byte(exceptionType)
	// 额外数据
	exData := &robotc.CmdExtend{}
	exData.LlTimeStamp = time.Now().UnixMilli()
	exData.StrUserName = []byte(username)
	resumeTaskCmd.ExData = exData
	dataBytes, err := proto.Marshal(resumeTaskCmd)
	if err != nil {
		logx.Error(context.Background(), "ResumeTaskCMDPack", logx.Err(err))
		return err
	}
	return pubsub.CMDPublish(robotID, "95", "resumeTask", base64.StdEncoding.EncodeToString(dataBytes))
}

// ResumeTask2Client 恢复时段任务
func (s *Server) ResumeTask2Client(in *robotc.ResumeTaskCMDParam, rsp robotc.BaseService_ResumeTask2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "resumeTask@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "ResumeTask2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "ResumeTask2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			resumeTaskCMD := &robotc.ResumeTaskCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, resumeTaskCMD)
			if err := rsp.Send(resumeTaskCMD); err != nil {
				logx.Error(context.Background(), "ResumeTask2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "ResumeTask2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// 删除任务封装
func DeleteTaskCMDPack(robotID string, mapName string, taskName string, username string) error {
	// 参数检查
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	if mapName == "" {
		return errorx.New("地图名称不能为空", -1)
	}
	if taskName == "" {
		return errorx.New("任务名称不能为空")
	}

	deleteTaskCmd := &robotc.DeleteTaskCMD{}
	deleteTaskCmd.StrMapName = []byte(mapName)
	deleteTaskCmd.StrTaskName = []byte(taskName)
	// 额外数据
	exData := &robotc.CmdExtend{}
	exData.LlTimeStamp = time.Now().UnixMilli()
	exData.StrUserName = []byte(username)
	deleteTaskCmd.ExData = exData
	dataBytes, err := proto.Marshal(deleteTaskCmd)
	if err != nil {
		logx.Error(context.Background(), "DeleteTaskCMDPack", logx.Err(err))
		return err
	}
	err = pubsub.CMDPublish(robotID, "95", "deleteTask", base64.StdEncoding.EncodeToString(dataBytes))
	return err
}

// 删除任务
func (s *Server) DeleteTaskCMD2Client(in *robotc.DeleteTaskCMDParam, rsp robotc.BaseService_DeleteTaskCMD2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "deleteTask@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "DeleteTaskCMD2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "DeleteTaskCMD2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			resumeTaskCMD := &robotc.DeleteTaskCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, resumeTaskCMD)
			if err := rsp.Send(resumeTaskCMD); err != nil {
				logx.Error(context.Background(), "DeleteTaskCMD2Client-sendCMD", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "DeleteTaskCMD2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// ShutdownCMDPack 关机命令封装
func ShutdownCMDPack(robotID string, username string) error {
	// 参数检查
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	// 判断设备是否存在
	var count int64
	mysql.NewDB().
		Model(&models.Device{}).
		Where(models.Device{
			RobotID: robotID,
		}).Count(&count)
	if count <= 0 {
		return errorx.New("设备不存在", -1)
	}

	shutdownCmd := &robotc.ShutdownCMD{}
	// 额外数据
	exData := &robotc.CmdExtend{}
	exData.LlTimeStamp = time.Now().UnixMilli()
	exData.StrUserName = []byte(username)
	shutdownCmd.ExData = exData
	dataBytes, err := proto.Marshal(shutdownCmd)
	if err != nil {
		logx.Error(context.Background(), "ShutdownCMDPack", logx.Err(err))
	}
	err = pubsub.CMDPublish(robotID, "95", "shutdown", base64.StdEncoding.EncodeToString(dataBytes))
	return err
}

// 关机
func (s *Server) Shutdown2Client(in *robotc.ShutdownCMDParam, rsp robotc.BaseService_Shutdown2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "shutdown@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "Shutdown2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "Shutdown2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			shutdownCMD := &robotc.ShutdownCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, shutdownCMD)
			if err := rsp.Send(shutdownCMD); err != nil {
				logx.Error(context.Background(), "Shutdown2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "Shutdown2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// 删除地图命令封装
func DeleteMapCMD(robotID string, mapName string, username string) error {
	// 参数检查
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	if mapName == "" {
		return errorx.New("地图名称不能为空", -1)
	}
	// 判断设备是否存在
	var count int64
	mysql.NewDB().
		Model(&models.Device{}).
		Where(models.Device{
			RobotID: robotID,
		}).Count(&count)
	if count <= 0 {
		return errorx.New("设备不存在", -1)
	}

	deleteMapCMD := &robotc.DeleteMapCMD{}
	deleteMapCMD.StrMapName = []byte(mapName)
	// 扩展信息
	exData := &robotc.CmdExtend{}
	exData.LlTimeStamp = time.Now().UnixMilli()
	exData.StrUserName = []byte(username)
	deleteMapCMD.ExData = exData
	dataBytes, err := proto.Marshal(deleteMapCMD)
	if err != nil {
		logx.Error(context.Background(), "deleteMapCMDPack", logx.Err(err))
	}
	err = pubsub.CMDPublish(robotID, "95", "deleteMap", base64.StdEncoding.EncodeToString(dataBytes))
	return err
}

// 删除地图
func (s *Server) DeleteMapCMD2Client(in *robotc.DeleteMapCMDParam, rsp robotc.BaseService_DeleteMapCMD2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "deleteMap@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "DeleteMapCMD2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "DeleteMapCMD2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			deleteMapCMD := &robotc.DeleteMapCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, deleteMapCMD)
			if err := rsp.Send(deleteMapCMD); err != nil {
				logx.Error(context.Background(), "DeleteMapCMD2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "DeleteMapCMD2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// ChargeCMDPack 充电命令封装
// 需要通过wildmsg下发
func ChargeCMDPack(robotID string, _ string) error {
	// 参数检查
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	// 判断设备是否存在
	var count int64
	mysql.NewDB().
		Model(&models.Device{}).
		Where(models.Device{
			RobotID: robotID,
		}).Count(&count)
	if count <= 0 {
		return errorx.New("设备不存在", -1)
	}
	// 充电模式设置
	reqCmdStartActionMode := &robotc.ReqCmdStartActionMode{}
	reqCmdStartActionMode.Mode = robotc.ECmdActionMode_eReCharging
	// wildmsg封装
	wm := &robotc.WildMsg{}
	wm.FromType = robotc.WildMsg_eMobileServiceClient
	wm.FromID = "server"
	wm.ToType = robotc.WildMsg_eMonitorClient
	wm.ToID = "95"
	wm.BodyType = string(reqCmdStartActionMode.ProtoReflect().Descriptor().FullName())
	var err error
	wm.BodyContent, err = proto.Marshal(reqCmdStartActionMode)
	if err != nil {
		logx.Error(context.Background(), "ChargeCMDPack", logx.Err(err))
	}
	wm.StrGUID = helper.RandString(16)
	bytes, err := proto.Marshal(wm)
	if err != nil {
		logx.Error(context.Background(), "ChargeCMDPack", logx.Err(err))
		return err
	}
	// 发布
	err = pubsub.CMDPublish(robotID, "95", "wildmsg", base64.StdEncoding.EncodeToString(bytes))
	return err
}

// 人工接管命令封装 TakeOverCMDPack
func TakeOverCMDPack(robotID string, status int, username string) error {
	// 参数检查
	if robotID == "" {
		return errorx.New("设备id不能为空", -1)
	}
	takeOverCMD := &robotc.TakeOverCMD{
		NTakeOverStatus: int32(status),
		ExData:          &robotc.CmdExtend{},
	}
	takeOverCMD.ExData.LlTimeStamp = time.Now().UnixMilli()
	takeOverCMD.ExData.StrUserName = []byte(username)
	dataBytes, _ := proto.Marshal(takeOverCMD)
	err := pubsub.CMDPublish(robotID, "95", "takeOver", base64.StdEncoding.EncodeToString(dataBytes))
	if err != nil {
		return err
	}
	// 等待命令执行结果
	resData, err := cmd.WaitCmdRsp(robotID, "takeOver", "0", "95")
	if err != nil {
		return err
	}
	// 解析响应的内容
	takseOverRsp := &robotc.RspTakeOver2ClientC2S{}
	if err := proto.Unmarshal(resData, takseOverRsp); err == nil {
		resCode := int(takseOverRsp.EnStatus)
		if resCode == 0 {
			return errorx.New("未知错误", -1)
		} else if resCode == 1 {
			return nil
		} else if resCode == 2 {
			return errorx.New("接管失败", -1)
		}
	}
	return err
}

// 人工接管 TakeOver2Client
func (s *Server) TakeOver2Client(in *robotc.TakeOverCMDParam, rsp robotc.BaseService_TakeOver2ClientServer) error {
	robotID := GetRobotID(rsp.Context())
	chanMapKey := "takeOver" + "@" + robotID + "@95"
	pubsub.RobotCMDChanMap.Store(chanMapKey, make(chan string, 1))
	defer pubsub.RobotCMDChanMap.Delete(chanMapKey)
	robotCMDChanInterface, _ := pubsub.RobotCMDChanMap.Load(chanMapKey)
	robotCMDChan, _ := robotCMDChanInterface.(chan string)
	logx.Info(context.Background(), "TakeOver2Client-connected", logx.String("robot_id", robotID))
	// 订阅对应主题
	for {
		select {
		case <-rsp.Context().Done():
			logx.Info(context.Background(), "TakeOver2Client-disconnected", logx.String("robot_id", robotID))
			return nil
		case data := <-robotCMDChan:
			cmd := &robotc.TakeOverCMD{}
			dataBytes, err := base64.StdEncoding.DecodeString(data)
			if err != nil {
				break
			}
			proto.Unmarshal(dataBytes, cmd)
			if err := rsp.Send(cmd); err != nil {
				logx.Error(context.Background(), "TakeOver2Client-sendCMDErr", logx.String("robot_id", robotID))
				return nil
			}
			logx.Info(context.Background(), "TakeOver2Client-sendCMD", logx.String("robot_id", robotID))
		}
	}
}

// 人工接管响应 TakeOverC2Srsp
func (s *Server) TakeOverC2Srsp(ctx context.Context, msg *robotc.RspTakeOver2ClientC2S) (*robotc.ReqTakeOver2ClientS2C, error) {
	dataBytes, _ := proto.Marshal(msg)
	pubsub.PublishCMDRsp(GetRobotID(ctx), "95", "takeOver", "0", dataBytes)
	return &robotc.ReqTakeOver2ClientS2C{NVoid: 1}, nil
}
